import pickle

import base64
from django.contrib.auth.models import AnonymousUser
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from django_redis import get_redis_connection
from rest_framework import status

from .serializers import CartSerializer, CartSKUSerializer, CartDeleteSerializer, CartSelectedAllSerializer
from goods.models import SKU
# import json


# Create your views here.
# url(r'^carts/$', views.CartView.as_view()),
class CartView(APIView):
    """购物车增删改查:  用户未登录和登录都可以访问"""

    def perform_authentication(self, request):
        """执行认证的方法: 默认请求在分发到视图方法之前就会对请求对象做认证
        如果认证时设置了权限,那么认证不通过就不会调用视图方法,并且返回401 (未授权)
        重写此方法之后,直接pass,表示不对本次请求做认证,认证的行为放在具体的业务逻辑需要的地方,程序员自己写代码认证
        """
        pass

    def post(self, request):
        """添加购物车"""
        # 创建序列化器进行反序列化
        serializer = CartSerializer(data=request.data)
        # 进行数据校验
        serializer.is_valid(raise_exception=True)

        # 读取出校验之后的数据
        sku_id = serializer.validated_data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.validated_data.get('selected')

        try:
            user = request.user  # 如果用户未认证直接获取请求中的user会有异常
        except Exception:
            user = None
        # response = Response(serializer.data, status=status.HTTP_201_CREATED)
        # 判断用户是否登录
        if user is not None and user.is_authenticated:
            # 已登录的存储数据到redis
            # 1.获取连接到redis的对象
            redis_conn = get_redis_connection('cart')

            # 2.创建redis管道
            pl = redis_conn.pipeline()

            # cart_dict = redis_conn.hgetall()
            # if sku_id in cart_dict:
            #     origin_count = cart_dict[sku_id]
            #     cart_dict[sku_id] = origin_count + count
            # 3.给redis购物车先做增量存储,计算
            # hincrby(self, name, key, amount=1)
            pl.hincrby('cart_%s' % user.id, sku_id, count)

            # 4.存储是否被勾选
            if selected:
                pl.sadd('selected_%s' % user.id, sku_id)

            # 5.执行管道
            pl.execute()

            # 6.响应
            return Response(serializer.data, status=status.HTTP_201_CREATED)

        else:
            """
            {
                sku_id10: {
                    "count": 10,  // 数量
                    "selected": True  // 是否勾选
                },
                sku_id20: {
                    "count": 20,
                    "selected": False
                },
                ...
            }
            """


            # 未登录的存储数据到cookie
            # 获取用户浏览器中的cookie购物车数据
            cart_str = request.COOKIES.get('cart')
            if cart_str: # 判断cookie中是否已经有购物车数据
                # 将购物车字符串转换成  bytes类型的购物车字符串
                cart_str_bytes = cart_str.encode()
                # 将bytes类型的购物车字符串转换为bytes类型的字典
                cart_dict_bytes = base64.b64decode(cart_str_bytes)
                # 将bytes类型的字典转换成 标准的python字典
                cart_dict = pickle.loads(cart_dict_bytes)
                # cart_dict = pickle.loads(base64.b64decode(cart_str.encode))
            else:
                cart_dict = {}  # 保存用户即使用是第一次使用cookie保存购物车数据,也有字典对象可以操作

            # 添加购物车数据到字典
            if sku_id in cart_dict:
                # 表示要添加的商品,在购物车已存在
                origin_count = cart_dict[sku_id]['count']
                count += origin_count

            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }

            # 需要将cart_dict数据转成字符串cookie_cart_str
            cookie_cart_dict_bytes = pickle.dumps(cart_dict)
            cookie_cart_str_bytes = base64.b64encode(cookie_cart_dict_bytes)
            cookie_cart_str = cookie_cart_str_bytes.decode()
            # cookie_cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()

            # 需要将cookie_cart_str数据写入到浏览器的cookie
            response = Response(serializer.data, status=status.HTTP_201_CREATED)
            response.set_cookie('cart', cookie_cart_str, expires=24*60*60)
            return response


    def get(self, request):
        """查询购物车"""
        # 判断用户是否登录
        try:
            user = request.user
        except Exception:
            user = None

        if user is not None and user.is_authenticated:
            # 用户已登录,操作redis购物车
            # 获取连接到redis的对象
            redis_conn = get_redis_connection('cart')
            pl = redis_conn.pipeline()
            # 查询redis中的购物车数据
            # 注意点: python3中的从redis读取的数据都是bytes类型的数据,python2中读取出来都是字符串
            pl.hgetall('cart_%s' % user.id)
            # 查询是否被勾选
            pl.smembers('selected_%s' % user.id)

            redis_cart_dict, redis_selected = pl.execute()

            # 定义空的字典
            cart_dict = {}
            for sku_id, count in redis_cart_dict.items():
                cart_dict[int(sku_id)] = {
                    'count': int(count),
                    'selected': sku_id in redis_selected  # 如果sku_id在redis_selected中,返回True,否则返回False
                }
        else:
            # 用户未登录,操作cookie购物车
            # 获取用户浏览器中的cookie购物车数据
            cart_str = request.COOKIES.get('cart')
            if cart_str:
                # 将购物车字符串转bytes类型的购物车字符串
                cart_str_bytes = cart_str.encode()
                # 将bytes类型的购物车字符串转换成bytes类型的字典
                cart_dict_bytes = base64.b64decode(cart_str_bytes)
                # 将bytes类型的字典转成Python标准的字典;登录和未登录cart_dict的变量名字要统一，后面统一处理
                cart_dict = pickle.loads(cart_dict_bytes)
            else:
                cart_dict = {}

        # 使用序列化器对redis购物车或cookie购物车 进行统一的序列化
        sku_ids = cart_dict.keys()
        skus = SKU.objects.filter(id__in=sku_ids)

        # 需要给每一个SKU增加count和selected属性
        for sku in skus:
            sku.count = cart_dict[sku.id]['count']
            sku.selected = cart_dict[sku.id]['count']

        # 创建序列化器对象,完成序列化
        serializer = CartSKUSerializer(skus, many=True)
        return Response(serializer.data)




    def put(self, request):
        """修改购物车"""
        # 使用序列化器对购物车数据进行校验
        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # 读取校验之后的数据
        sku_id = serializer.validated_data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.validated_data.get('selected')
        # sku_id, count, selected = serializer.validated_data.values()

        # 判断用户是否登录
        try:
            user = request.user
        except Exception:
            user = None

        if user is not None and user.is_authenticated:
            # 用户已登录,操作redis购物车
            # 获取连接到redis的对象
            redis_conn = get_redis_connection('cart')
            pl = redis_conn.pipeline()

            # 操作redis修改购物车数据:将已存在的商品数量覆盖即可
            pl.hset('cart_%s' % user.id, sku_id, count)

            # 是否勾选
            if selected:
                pl.sadd('selected_%s' % user.id, sku_id)
            else:
                pl.srem('selected_%s' % user.id, sku_id)

            # 执行管道
            pl.execute()
            return Response(serializer.data)

        else:
            # 用户未登录,操作cookie购物车
            # 获取cookie购物车数据
            cart_str = request.COOKIES.get('cart')

            if cart_str:
                # 将购物车字符串转bytes类型的购物车字符串
                cart_str_bytes = cart_str.encode()
                # 将购物车字符串bytes类型转换bytes类型的字典
                cart_dict_bytes = base64.b64decode(cart_str_bytes)
                # 将购物bytes类型的字典转换成Python字典
                cart_dict = pickle.loads(cart_dict_bytes)
            else:
                cart_dict = {}  # 保证用户即使第一次使用cookie保存购物车数据,也有字典对象可以操作

            # 添加购物车数据到字典
            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }

            # 需要将cart_dict数据转成字符串cookie_cart_str
            cookie_cart_dict_bytes = pickle.dumps(cart_dict)
            cookie_cart_str_bytes = base64.b64encode(cookie_cart_dict_bytes)
            cookie_cart_str = cookie_cart_str_bytes.decode()

            # 需要将cookie_cart_str数据写入到浏览器的cookie
            response = Response(serializer.data)
            response.set_cookie('cart', cookie_cart_str)
            return response

    def delete(self, request):
        """删除购物车"""
        serializer = CartDeleteSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        sku_id = serializer.validated_data.get('sku_id')

        try:
            user = request.user
        except Exception:
            user = None
        response = Response(status=status.HTTP_204_NO_CONTENT)
        if user is not None and user.is_authenticated:
            # 用户已登录
            redis_conn = get_redis_connection('cart')
            pl = redis_conn.pipeline()
            # 删除购物车记录
            pl.hdel('cart_%s' % user.id, sku_id)
            # 移除选中状态
            pl.srem('selected_%s' % user.id, sku_id)
            pl.execute()
        else:
            # 未登录
            cart_str = request.COOKIES.get('cart')
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                cart_dict = {}

            if sku_id in cart_dict:
                del cart_dict[sku_id]

            if len(cart_dict.values()):
                cookie_cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
                response.set_cookie('cart', cookie_cart_str)
            else:
                response.delete_cookie('cart')

        return response

class CartSelectedAllView(APIView):
    """购物车全选视图"""

    def perform_authentication(self, request):
        """默认视图在进行请求分发时就会进行认证
        在视图中重写此方法,如果内部直接pass,表示在请求分发时,先不要认证,让请求可以正常访问
        目的:延后它的认证,为了让未登录用户也能先访问我的视图
        将来自己去写认证,
        """
        pass


    def put(self, request):

        # 创建序列化器(进行反序列化)对前端传过来的是否全选数据进行校验
        serializer = CartSelectedAllSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # 获取校验后面selected数据
        selected = serializer.validated_data.get('selected')

        try:
            user = request.user
        except Exception:
            user = None

        if user is not None and user.is_authenticated:  # 判断当前是不是登录用户
            # 登录用户,操作redis购物车
            redis_conn = get_redis_connection('carts')
            # 获取购物车商品数据
            redis_cart_dict = redis_conn.hgetall('cart_%s' % user.id)
            sku_id_keys = redis_cart_dict.keys()  # 取出字典中所有的key
            if selected:  # 成立表示要全选
                redis_conn.sadd('selected_%s' % user.id, *sku_id_keys)
            else:  # 全部不勾选
                redis_conn.srem('selected_%s' % user.id, *sku_id_keys)
            # 响应
            return Response(serializer.data)
        else:
            # 未登录用户,操作cookie购物车
            cart_str = request.COOKIES.get('cart')
            if cart_str:  # 判断cookie是否有值
                cookie_cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                cookie_cart_dict = {}
                """
                {
                    sku_id_1: {
                        'count': count,
                        'selected: True
                    },
                    sku_id_2: {
                        'count': count,
                        'selected: True
                    }
                }
                """

            for sku_dict in cookie_cart_dict.values():
                sku_dict['selected'] = selected  # 拿到里面的每个小字典,把它们的selected改成全选或全部不选

            # 把购物车字典转换成购物车字符串
            cookie_cart_str = base64.b64encode(pickle.dumps(cookie_cart_dict)).decode()
            # 创建响应对象
            response = Response(serializer.data)
            # 设置cookie
            response.set_cookie('cart', cookie_cart_str)
            return response